/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       army_list_window.h

	$Header: /game/army_list_window.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "army_list_window.h"

#include "adaptor_handler.h"
#include "army.h"
#include "army_list.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_cache_window.h"
#include "bound_handler.h"
#include "button.h"
#include "external_string.h"
#include "format_string.h"
#include "hero.h"
#include "interface_help.h"
#include "item_help_block.h"
#include "replace_keywords.h"
#include "scrollbar.h"
#include "town_properties.h"

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
extern t_bitmap_group_cache g_army_rings( "adventure.army_rings" );

t_army_list_window::t_army_list_window( t_screen_rect const& rect, t_window* parent, bool hide_unused )
                  : t_window( rect, k_completely_transparent, parent )
{
	m_bitmaps = g_army_rings.get();
	m_armies = 0;
	m_scrollbar = 0;
	m_first_army = 0;
	m_selected_army = -1;
	m_hide_unused = hide_unused;

	// build top item
	t_screen_point portrait_point = m_bitmaps->find( "portrait" )->get_rect().top_left();

	t_bitmap_layer const* empty_background	= m_bitmaps->find( "empty_background" );
	t_bitmap_layer const* move_bar			= m_bitmaps->find( "move_bar" );
	t_bitmap_layer const* strength_bar		= m_bitmaps->find( "mana_bar" );
	t_bitmap_layer const* frame				= m_bitmaps->find( "army_frame" );
	t_bitmap_layer const* frame_highlight	= m_bitmaps->find( "army_frame_highlight" );

	int	height = rect.height();
	int	frame_height = frame->get_rect().height() - 4;

	t_screen_point        point(0,0);
	while (point.y + frame_height <= height)
	{
		add_item( point, move_bar, strength_bar, portrait_point, frame, frame_highlight, empty_background );
		point.y += frame_height;
	}

	int i;

	for (i = 0; i < m_items.size(); i++)
		m_items[i].button->move_to_back();
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::add_item( t_screen_point point, 
								   t_bitmap_layer const* move_bar,
							       t_bitmap_layer const* strength_bar,
								   t_screen_point portrait_point,
							       t_bitmap_layer const* frame, 
							       t_bitmap_layer const* frame_highlight,
								   t_bitmap_layer const* empty_background )
{
	t_item item;
	int    index = m_items.size();
	t_screen_rect rect;

	item.army = 0;

	item.button           = new t_button( point + portrait_point, this );

	item.empty_background = new t_bitmap_layer_window( empty_background, point, this );

	rect = move_bar->get_rect() + point;
	item.move_background  = new t_window( rect, k_some_transparency, this );
	item.move_clipper     = new t_window( rect - rect.top_left(), k_completely_transparent,
		                                  item.move_background );
	item.move_bar         = new t_bitmap_layer_window( move_bar, t_screen_point(0,0), 
												       item.move_clipper, false );
	rect = strength_bar->get_rect() + point;
	item.strength_background  = new t_window( rect, k_some_transparency, this );
	item.strength_clipper     = new t_window( rect - rect.top_left(), k_completely_transparent,
		                                  item.strength_background );
	item.strength_bar		  = new t_bitmap_layer_window( strength_bar, t_screen_point(0,0), 
		                                               item.strength_clipper, false );
	item.portrait         = new t_bitmap_layer_cache_window( t_bitmap_layer_ptr(), 
	                                                 t_screen_point(0,0), item.button );
	item.button->set_released_image( item.portrait );
	item.button->set_click_handler( add_2nd_argument( 
							   bound_handler( *this, &t_army_list_window::select ), index ) );
	item.button->set_double_click_handler( add_2nd_argument(
		bound_handler( *this, &t_army_list_window::double_click ), index ) );

	item.normal_border = new t_bitmap_layer_window( frame, point, this );
	item.highlight_border = new t_bitmap_layer_window( frame_highlight, point, this );
	m_items.push_back( item );
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
t_army_list_window::~t_army_list_window()
{
	if (m_armies != 0)
		m_armies->detach( this );
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::attach( t_scrollbar* scrollbar )
{
	m_scrollbar = scrollbar;
	if (scrollbar == 0)
		return;

 	scrollbar->set_handler( bound_handler( *this, &t_army_list_window::on_scroll ) );
	set_scrollbar();
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
inline int t_army_list_window::get_max_index()
{
	const int maximum = get_num_armies() - get_num_rings();
	if (maximum < 0)
		return 0;
	return maximum;
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::set_scrollbar()
{
	// Handle the limit callback if necessary
	int limit_flags;

	if (m_first_army == 0)
		limit_flags = k_army_list_window_at_top;
	else
		limit_flags = 0;
	
	if (m_first_army == get_max_index())
		limit_flags |= k_army_list_window_at_bottom;

	m_limit_handler( limit_flags );
	

	// Update the scroll bar limits and position
	if (m_scrollbar)
	{
		m_scrollbar->set_limits( 0, get_max_index() );
		m_scrollbar->set_position( m_first_army );
		m_scrollbar->set_visible( is_visible() && get_max_index() > 0 );
	}
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::detach()
{
	m_armies = 0;
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::inserted( int index )
{
	if (index < m_first_army)
		m_first_army++;
	if (index < m_selected_army)
		m_selected_army++;
	set_scrollbar();
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::removed( int index )
{
	if (index <= m_first_army && m_first_army > 0)
		m_first_army--;
	if (index == m_selected_army)
		m_selected_army = -1;
	if (index <= m_selected_army && m_selected_army >= 0)
		m_selected_army--;
	set_scrollbar();
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::set_army_array( t_army_array* list )
{
	if (list == m_armies)
		return;
	if (m_armies != 0)
		m_armies->detach( this );
	list->attach( this );
	m_armies = list;
	m_first_army = 0;
	m_selected_army = -1;
	update();
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
static void set_bar_height( int value, int max_value, t_window* bar,
						   t_window* clipper, t_window* background,
						   std::string const& help_text )
{
	int maximum			= bar->get_height();
	int height			= value * maximum / max_value;
	t_screen_rect rect	= clipper->get_screen_rect();

	if (height > maximum)
		height = maximum;
	rect.top = rect.bottom - height;
	clipper->move_screen_rect( rect, false );
	bar->set_help_balloon_text( help_text );
	background->set_help_balloon_text( help_text );
}

//static t_external_string const k_text_movement_help( "shared.army_ring_distance" );
//static t_external_string const k_text_strength_help( "shared.army_ring_strength" );

static t_external_string const k_text_right_click_hero( "army_list_right_click_hero.misc" );
static t_external_string const k_text_right_click_creature( "army_list_right_click_creature.misc" );

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------

void t_army_list_window::update()
{
	int item_index;
	int army_index;
	int value;
	int maximum;
	std::string text;

	army_index = m_first_army;
	item_index = 0;
	t_help_block const&		shared_help = get_help_block( "shared" );

	while (army_index < m_armies->size() && item_index < m_items.size())
	{
		t_army* army			       = (*m_armies)[army_index];

		t_creature_stack const& leader = army->get_creatures().get_leader();
		t_item& item = m_items[item_index];

		item.army = army;
		item.portrait->set_bitmap( leader.get_portrait() );
		item.portrait->set_help_balloon_text( leader.get_name() );

		// create the right click help
		t_hero const * hero = leader.get_hero();
		if( hero )
		{
			std::string first_part;
			first_part = replace_keywords( k_text_right_click_hero, "%name", hero->get_name(), 
										"%level", format_string( "%i", hero->get_level() ), "%class", hero->get_class_name() );
			text = replace_keywords( first_part, "%align", get_alignment_name( hero->get_alignment() ) );
		} else
			text = replace_keywords( k_text_right_click_creature, "%number", format_string( "%i", leader.get_number() ),
										"%name", leader.get_name() );
		item.portrait->set_right_click_text( text );

		if (item.portrait->get_bitmap())
			item.empty_background->set_visible( false );
		else
			item.empty_background->set_visible( true );

		value = army->get_movement();
		if (value < army->get_next_step_cost())
			value = 0;
		text = replace_keywords( shared_help.get_name("army_ring_distance"), "%distance", 
			                     format_string( "%i", value / 100 ));
		set_bar_height( value, 1500, item.move_bar, item.move_clipper,
			            item.move_background, text );
		item.move_bar->set_right_click_text( shared_help.get_help( "army_ring_distance" ) );

		value = army->get_creatures().get_experience_value();
		value = (log( (double)value ) - log( (double)k_minimum_army_value )) * k_log_multiplier;
		maximum = (log( (double)k_maximum_army_value ) - log( (double)k_minimum_army_value)) * k_log_multiplier;
		set_bar_height( value, maximum, item.strength_bar, item.strength_clipper,
						item.strength_background, shared_help.get_name("army_ring_strength") );
		item.strength_bar->set_right_click_text( shared_help.get_help("army_ring_strength") );

		item.button->set_visible( true );
		item.move_background->set_visible( true );
		item.strength_background->set_visible( true );

		item_index++;
		army_index++;
	}

	// Deal with any empty items
	while (item_index < m_items.size())
	{
		t_item& item = m_items[item_index];

		item.army = 0;
		item.button->set_visible( false );
		item.move_background->set_visible( false );
		item.strength_background->set_visible( false );

		if (m_hide_unused && item_index > 0)
		{
			// Hide the rings entirely if this isn't the very first ring
			item.normal_border->set_visible( false );
			item.highlight_border->set_visible( false );
			item.empty_background->set_visible( false );
		}
		else
		{
			item.empty_background->set_visible( true );
		}

		item_index++;
	}

	set_scrollbar();
	set_highlight( m_selected_army );
}

// ----------------------------------------------------------------------
// display a list of armies -- index is an offset into
// the m_armies array.
// ----------------------------------------------------------------------
void t_army_list_window::set_highlight( int index )
{
	int i;

	m_selected_army = index;
	index = m_selected_army - m_first_army;
	for (i = 0; i < m_items.size(); i++)
	{
		t_item& item = m_items[i];

		const bool see_normal = (i != index) && 
								(!m_hide_unused || i==0 || i < m_armies->size());

		item.normal_border->set_visible( see_normal );
		item.highlight_border->set_visible( i == index );
	}
}

// ----------------------------------------------------------------------
// display a list of armies -- item_index is an offset into the m_items
// array (the visible rings).
// ----------------------------------------------------------------------
void t_army_list_window::select( t_button* button, int item_index )
{
	if (m_armies == 0)
		return;
	if (item_index + m_first_army >= m_armies->size())
		return;

	set_highlight( m_first_army + item_index );
	m_select_handler( (*m_armies)[m_selected_army] );
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::double_click( t_button* button, int result )
{
	if (m_armies == 0)
		return;
	if (result + m_first_army >= m_armies->size())
		return;

	m_double_click_handler( (*m_armies)[m_first_army + result] );
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::on_scroll( t_scrollbar* scrollbar, int first_value )
{
	if (m_first_army == first_value)
		return;

	m_first_army = first_value;
	update();
}

// ----------------------------------------------------------------------
// select an army
// ----------------------------------------------------------------------
void t_army_list_window::select_army( t_army* army )
{
	if (m_selected_army >= 0 && (*m_armies)[m_selected_army].get() == army)
		return;

	int index;

	index = m_armies->size();
	while (index--)
	{
		if ((*m_armies)[index].get() == army)
			break;
	}

	// If the army isn't already visible, move the list so that it becomes visible
	if (index < m_first_army)
	{
		set_first_visible( index );
	}
	else if (index >= m_first_army + m_items.size())
	{
		set_first_visible( index - m_items.size() + 1 );
	}

	set_highlight( index );
}


// ----------------------------------------------------------------------
// Set the first visible army in the list -- index is an offset into
// the m_armies array.
// ----------------------------------------------------------------------
void t_army_list_window::set_first_visible( int index )
{
	if (index < 0)
		index = 0;

	if (index > get_max_index())
		index = get_max_index();
	
	m_first_army = index;
	update();
}


// ----------------------------------------------------------------------
// Move the selection N slots but never stop on a sleeping army
// ----------------------------------------------------------------------
void t_army_list_window::move_selection_over_sleepers( int step_size, bool wrap_selection )
{ 
	t_army * army = 0;
	int increment_count = 0; // avoid infinite loops

	do {
		army = move_selection( step_size, wrap_selection );
		increment_count += step_size;
	} while( increment_count < m_armies->size() && army->get_sleeping() );

	select( 0, m_selected_army - m_first_army );
}

// ----------------------------------------------------------------------
// Move the selection N slots from its current position in the list
// ----------------------------------------------------------------------
t_army * t_army_list_window::move_selection( int step_size, bool wrap_selection )
{
	if (!m_armies || m_armies->empty())
		return 0;
	
	int index = m_selected_army + step_size;

	if( index < 0 )
	{
		if( wrap_selection )
			index = m_armies->size() - 1;
		else
			index = 0;
	}
	if( index >= m_armies->size() )
	{
		if( wrap_selection )
			index = 0;
		else
			index = m_armies->size() - 1;
	}

	t_army* army = (*m_armies)[index].get();
	select_army( army );

	return (*m_armies)[m_selected_army];
}

// ----------------------------------------------------------------------
// display a list of armies
// ----------------------------------------------------------------------
void t_army_list_window::on_visibility_change()
{
	if ( m_scrollbar != 0 )
		m_scrollbar->set_visible( is_visible() && get_max_index() > 0 );
}
